#ifndef BOOTSTRAP_H_
#define BOOTSTRAP_H_

#include <vector.h>
#include "util.h"

using namespace std;

static int random_range_low 	= 0;
static int random_range_high	= 10;
		
template <class T>
class Bootstrap
{	
private:
	vector<T> samples;
	int N_all_samples, n_b_samples, b_bootstrap;
	float alpha_confidence_interval;	
	
public:
	/** default constructor. do nothing */
	Bootstrap(){};
	/**
	 * @param N number of samples
	 * @param n number of bootstrap samples
	 * @param b number of bootstrap times
	 * @param alpah confindence interval range
	 */
	//Bootstrap(int N, int n, int b, float alpha);	
	Bootstrap(int N, int n, int b, float alpha):N_all_samples(N), n_b_samples(n), b_bootstrap(b), alpha_confidence_interval(alpha){
		Util _util;
				
		samples = _util.generate_random_numbers<T>(N, random_range_low, random_range_high);
		 
		cout << "a new bootstrap object is created" << endl;
		cout << "N :" << N << ", n: " << n << ", b: " << b << ", alpha: " << alpha << endl;	
		 //_util.print_vector( samples );
	};	
	
	virtual ~Bootstrap(){
		//do sth.
	};
	
	/** 
	 * @return bootstrap variance
	 */ 
	float do_bootstrap(vector< vector<T> > *b_samples_vector, vector<float> *b_variances);
	
	/** TODO change to private when release 
	 * @param seed random seed
	 * @param bootstrap_samples bootstrap samples
	 */
	float do_one_bootstrap(unsigned seed, vector<T> *b_samples);
	
	/**
	 * @return sorted means of b_samples_vector
	 */
	vector<float> compute_confidence_interval(vector< vector<T> > b_samples_vector, 
														vector<float> *upper_bound, 
														vector<float> *lower_bound);
	
	/**
	 * Get one set of bootstrap samples.
	 * 
	 * note: a specfied random seed is to avoid repeat the same random numbers generated.
	 */
	vector<T> get_one_b_samples(unsigned seed);
	
	/**
	 * @param n number of bobtw [0, 100]otstrap samples in each bootstrap
	 * @param b number of bootstraps
	 */	 
	vector< vector<T> > get_bootstrap_samples(int n, int b);	
};

template <class T>
vector<float> Bootstrap<T>::compute_confidence_interval(vector< vector<T> > b_samples_vector, 
														vector<float> *upper_bound, 
														vector<float> *lower_bound){
	vector<float> means;
	
	typename vector< vector<T> >::iterator pos;
	for (pos=b_samples_vector.begin(); pos!=b_samples_vector.end(); ++pos){
		vector<T> b_samples	= *pos;
		Util _util;
		float mean = _util.compute_mean(b_samples);
		means.push_back(mean);
	}
	
	std::sort(means.begin(), means.end());
	
	int bound_range = (int)(alpha_confidence_interval * means.size());
	
	vector<float>::iterator
		begin_pos=means.begin(), 
		end_pos=--means.end();
	for (int i=0; i < bound_range; i++){
		float upper_mean	= (float)(*begin_pos);
		upper_bound->push_back(upper_mean);
		++begin_pos;
		
		float lower_mean	= (float)(*end_pos);
		lower_bound->push_back(lower_mean);
		--end_pos;
	}
	
	return means;
}
	
template <class T>
float Bootstrap<T>::do_bootstrap(vector< vector<T> > *b_samples_vector, vector<float> *b_sample_variances){
	vector<T> b_samples;
	
	srand(time(NULL));
	for (int i = 0; i < b_bootstrap; i++){
		float variance = do_one_bootstrap(random(), &b_samples);		
		b_sample_variances->push_back(variance);		
		b_samples_vector->push_back(b_samples);
	}
	
	Util _util;
	float bootstrap_variance = _util.compute_variance<float>(*b_sample_variances);
	
	return bootstrap_variance; 
}

template <class T>
float Bootstrap<T>::do_one_bootstrap(unsigned seed, vector<T> *b_samples){	
	*b_samples = get_one_b_samples(seed);
	Util _util;
	float variance = _util.compute_variance(*b_samples);
	
	return variance; 
}

template <class T>
vector<T> Bootstrap<T>::get_one_b_samples(unsigned seed){
	vector<T> b_samples;
	Util _util;
	
	vector<T> v_random_pos;

	srand(seed);
	while (true){		
		if ((int)v_random_pos.size() == n_b_samples) break;

		int random_pos = (((int)(((double)rand()/(double)(RAND_MAX+1))*N_all_samples))*-1);
		if (!_util.is_contain(v_random_pos, random_pos)){
			v_random_pos.push_back(random_pos);
			b_samples.push_back(samples.at(random_pos));
			//cout << "add a new rand_pos " << random_pos << endl;
		}else{
			//cout << random_pos << " is already drawn" << endl;
		}		
	}

	return b_samples;
}

#endif /*BOOTSTRAP_H_*/
